Laboratorium 4 - zadania

Jeśli nie posiadasz jeszcze pakietu skimage, zainstaluj go poniższym poleceniem:

Zadanie 1

Dane są obrazy hare i pills. Wykonaj dla nich segmentację przy użyciu algorytmu SLIC. Celem jest uzyskanie jednolitych superpikseli, a więc zawierających tylko fragmenty obiektów lub tylko tła (a nie jednego i drugiego naraz). Przeanalizuj zachowanie algorytmu dla różnej liczby superpikseli i parametru sigma.

Zwróć uwagę, że wynikiem działania scikitowej implementacji SLIC (podobnie jak wielu innych algorytmów klasyfikacyjnych) jest obraz typu np.int64 przyporządkowujący każdemu pikselowi obrazu wejściowego indeks superpiksela. Takiej reprezentacji oczekuje też funkcja mark_boundaries.

Wraz ze zwiększaniem ilości klastrów dostajemy lepsze odwzorowanie zawartości obrazu - w ramach jednego super-piksela obraz jest w miarę jednorodny, nie ma dwóch obiektów w jednym klastrze. Jednak im mniejsze super-piksele tym mniej w nich kontekstu i potencjalne cechy z nich wydobywane są bardziej zaszumione.

Ze wzrostem parametru sigma (wielkość kernela gaussa, którym jest wygładzany obraz w ramach preprocessingu), super-piksele mają bardziej regularne kształty i mniej zaszumione krawędzie. Jednak taka regularyzacja sprawia, że przy większych wartościach parametru wymuszana regularność klastrów nie pozwala im na odpowiednie dopasowanie się do krawędzi obiektów.

Zadanie 2

Dla obrazu hare wykonaj post-processing segmentacji SLIC by uzyskać dwa jednorodne obszary: jeden zawierający wyłącznie zająca, drugi zawierający wyłącznie trawę. Wykorzystaj dowolny algorytm klasteryzacji (np. cv2.kmeans) używając cech superpikseli uzyskanych w zadaniu 1 jako danych wejściowych. Najprostszymi cechami, którymi można opisać superpiksele, są np. średnie wartości składowych barwy (pikseli, z których składa się superpiksel) - od nich zacznij. Zawsze możesz rozszerzyć rozwiązanie o bardziej wyrafinowane pomysły, jeśli starczy czasu.

Zaprezentuj wyniki najlepiej za pomocą mark_boundaries albo po prostu wyświetlając maskę binarną.


NumPy pro-tip: aby uzyskać zbiór wszystkich pikseli obrazu o wartości np. 3 możesz użyć notacji: obraz==3. Wynikiem jest macierz boolowska, której można używać jako selektor. Np. aby na obrazie A wyzerować wszystkie piksele, którym w obrazie B odpowiada wartość 5, możesz napisać: A[B==5] = 0. Ten zapis jest znacznie szybszy niż manualne iterowanie po obrazach i sprawdzanie warunków if-ami.

Przykładowe użycie cv2.kmeans (więcej możesz poczytać np. tutaj):

c, labels, centers = cv2.kmeans(
    data=data,  # np.ndarray typu float o wymiarach NxK gdzie N to liczba sampli (u nas: superpikseli), K - liczba cech
    K=2,        # oczekiwana liczba klastrów
    bestLabels=None,
    criteria=(cv2.TERM_CRITERIA_EPS, 10, 1.0),  # przykładowe kryteria stopu
    attempts=1, # liczba powtórzeń algorytmu
    flags=cv2.KMEANS_RANDOM_CENTERS  # sposób inicjalizacji klastrów
)

Funkcja ta zwraca krotkę, której najważniejszym komponentem jest ten drugi (tu: labels), tj. macierz o wymiarach Nx1, zawierająca indeks klastra przypisanego i-temu samplowi.

SLIC + RGB, KMEANS + G

SLIC + HSV, KMEANS + V

SLIC + Lab, KMEANS + L

Superpiksele są najbardziej nieregularne dla HSV. Dla Lab i RGB klastry są regularne i dobrze oddają krawędzi królika. Dodatkowo warto zauważyć, że dobór kanałów dla algorytmu KMEANS jest istotny - obrazuje to test ablacyjny poniżej. Dla samego kanału V wyniki są lepsze niż dla wszystkich kanałów.

HSV: tylko kanał V do KMEANS

HSV: wszystkie kanały do KMEANS